# -*- shell-script -*-

set -eu
[ x"${GMB_SHELL_DEBUG:-no}" = x"no" ] || set -x


GMB_LOGGING_ERROR=0
GMB_LOGGING_WARNING=1
GMB_LOGGING_INFO=2
GMB_LOGGING_DEBUG=3

GMB_LOGGING_CMDERR=0
GMB_LOGGING_CMDOUT=1

GMB_LOGGING_0='ERROR'
GMB_LOGGING_1='WARNING'
GMB_LOGGING_2=''
GMB_LOGGING_3='DEBUG'

# gmb_log LEVEL MSG
gmb_log()
{
    local lvl=${1}
    shift
    local msg="${@}"
    
    if [ "${lvl}" -le ${GMB_VERB_LEVEL} ]; then
        local lvlname=$(eval "echo \${GMB_LOGGING_${lvl}}")
        [ x"${lvlname}" = x ] || lvlname="${lvlname}:"
        echo "gmb:${lvlname} ${msg}" >&2
    fi

    if [ x"${GMB_LOGFILE:-}" != x ]; then
        echo "${msg}" >>"${GMB_LOGFILE}"
    fi
}
        
gmb_debug()   { gmb_log ${GMB_LOGGING_DEBUG} "${@}"; }
gmb_info()    { gmb_log ${GMB_LOGGING_INFO} "${@}"; }
gmb_warning() { gmb_log ${GMB_LOGGING_WARNING} "${@}"; }
gmb_error()   { gmb_log ${GMB_LOGGING_ERROR} "${@}"; exit 1; }

gmb_run_trace() { gmb_debug "RUN-TRACE: ${@}"; }


# gmb_strup
#
gmb_strup()
{
    sed -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'
}


# gmb_strstrip
#
gmb_strstrip()
{
    sed -e 's,^[ \t\n\f\r]\+,,g; s,[ \t\n\f\r]\+$,,g; s,[ \t\n\f\r]\+, ,g'
}


# gmb_strsplit
#
gmb_strsplit()
{
    local delim="${1}"
    sed -e "s/${delim}/ /g"
}


# gmb_env_push [-p]
#
gmb_env_push()
{
    local print=0
    while [ ${#} -gt 0 ]; do
        case "${1}" in
            -p) print=1; shift;;
            *) gmb_error "gmb_env_push: invalid option: '${1}'";;
        esac
    done
    
    [ -d "${GMB_ENV_STACK_DIR}" ] || mkdir -p "${GMB_ENV_STACK_DIR}"
        
    local stack=$(mktemp --tmpdir="${GMB_ENV_STACK_DIR}")
    # gmb_info "** ENV PUSH : ${stack} **"
    
    local filter_expr=$(echo "${GMB_ENV_STACK_IGNORE}" | sed -e 's, ,\\|,g')
    # [FIXME] not sure quote handling is bullet proof here
    local expr="\
/^\(${filter_expr}\)=.*\$/d;
s,',\\',g;
s,^\([a-zA-Z0-9_]\+\)=\(.*\)\$,export \1='\2',g"

    env --null \
        | sed -z -e "${expr}" \
        | tr '\0' '\n' \
             >>"${stack}"

    local prev=
    if [ -e "${GMB_ENV_STACK_TAIL}" ]; then
        prev=$(realpath "${GMB_ENV_STACK_TAIL}")
    fi
    # note: no need to export this one
    echo "GMB_ENV_STACK_PREV='${prev}'" >>"${stack}"
    ln -sf "${stack}" "${GMB_ENV_STACK_TAIL}"
    [ -e "${GMB_ENV_STACK_HEAD}" ] \
        || ln -s "${stack}" "${GMB_ENV_STACK_HEAD}"

    [ ${print} -eq 0 ] || printf '%s' "${stack}"
}


# gmb_env_pop
#
gmb_env_pop()
{
    [ -e "${GMB_ENV_STACK_TAIL}" ] || gmb_error "gmb_env_pop: no stack"

    local stack=$(realpath "${GMB_ENV_STACK_TAIL}")
    # gmb_info "** ENV POP: ${stack} **"

    __gmb_env_reset "${stack}"

    if [ x"${GMB_ENV_STACK_PREV}" = x ]; then
        rm "${GMB_ENV_STACK_HEAD}" "${GMB_ENV_STACK_TAIL}"
    else
        ln -sf "${GMB_ENV_STACK_PREV}" "${GMB_ENV_STACK_TAIL}"
    fi
    rm "${stack}"
    unset GMB_ENV_STACK_PREV
}


# gmb_env_reset
#
gmb_env_reset()
{
    local stack="${1:-}"

    [ x"${stack}" = x -o -e "${stack}" ] || error "gmb_env_reset: stack does not exist: '${stack}'"
    __gmb_env_reset "${stack}"
    unset GMB_ENV_STACK_PREV
}


# __gmb_env_reset
#
__gmb_env_reset()
{
    local stack="${1}"

    # unset all except ignored vars
    local filter_expr=$(echo "${GMB_ENV_STACK_IGNORE}" | sed -e 's, ,\\|,g')
    local expr="
/^\(${filter_expr}\)=.*\$/d;
s,^\([a-zA-Z0-9_]\+\)=.*\$,unset \1,g"

    local unset_list=$(env --null \
                           | sed -z -e "${expr}" \
                           | sort -z \
                           | tr '\0' '\n')

    eval "${unset_list}"

    if [ x"${stack}" != x ]; then
        . "${stack}"
    fi
}


# gmb_env_exec
#
gmb_env_exec()
{
    local env_files=
    [ x"${GMB_TARGET_NAME:-}" = x ] \
        || env_files="${env_files} ${GMB_TARGETS_CONFIG_DIR}/${GMB_TARGET_NAME}.env"

    # gmb_env_reset

    # echo "ENV FILES:" >&2
    for f in ${env_files}; do
        if [ -f "${f}" ]; then
            # echo "  ${f}" >&2
            . "${f}"
        fi
    done
    
    # env | sort >&2
}


# gmb_exec [-c CWD] COMMAND [ARGS...]
#
gmb_exec()
{
    local cmd_r=0
    ( gmb_exec_real "${@}"; ) || cmd_r=$?
    gmb_debug "gmb_exec: command status: ${cmd_r}"
    return $cmd_r
}


# gmb_exec_real
#
gmb_exec_real()
{
    local cwd=
    local cmd=
    while [ ${#} -gt 0 ]; do
        case "${1}" in
            -c)
                cwd="${2}"
                shift 2
                ;;
            *)
                cmd="${1}"
                shift
                break
        esac
    done

    # [FIXME] what's a good default? we don't want to run random
    # commands in $HOME or something
    : ${cwd:=${GMB_RUN_DIR}}

    [ -d "${cwd}" ] || gmb_error "gmb_exec: cwd does not exist: '${cwd}'"

    gmb_info "${cwd}> ${cmd} ${@}"
    
    # create pipes
    local pipe_dir=$(mktemp --tmpdir="${GMB_RUN_DIR}" -d)
    local pipe_out="${pipe_dir}/stdout"
    local pipe_err="${pipe_dir}/stderr"
    mkfifo "${pipe_out}" "${pipe_err}"
    
    if [ ${GMB_CMD_VERB_LEVEL} -gt ${GMB_LOGGING_CMDOUT} ]; then
        tee -a "${GMB_LOGFILE}" >&2 <"${pipe_out}" &
    else
        cat >>"${GMB_LOGFILE}" <"${pipe_out}" &
    fi
    if [ ${GMB_CMD_VERB_LEVEL} -gt ${GMB_LOGGING_CMDERR} ]; then
        tee -a "${GMB_LOGFILE}" >&2 <"${pipe_err}" &
    else
        cat >>"${GMB_LOGFILE}" <"${pipe_err}" &
    fi

    # env setup (we're in a subprocess, no need to push env)
    #
    # NOTE: don't expect any GMB_* var to be here after that
    #
    gmb_env_exec

    # we can remove the pipe files once they are connected
    exec >"${pipe_out}" 2>"${pipe_err}"
    rm -rf "${pipe_dir}"

    # [FIXME] how to quote args correctly?
    cd "${cwd}"
    local cmd_r=0
    "${cmd}" ${@} || cmd_r=$?

    exec 1>&- 2>&-
    wait

    return $cmd_r
}
